
#include "ssdsim_cache.h"
#include <cstdlib>

Cache_dev::Cache_dev(int csize, FILE * output){
	cache_size = csize;
	outfile = output;

	curr_size = 0;

	cache_stat = new Cache_stat();
	cache_stat->cache_hit = 0;
	cache_stat->cache_miss = 0;

	cache = new Cache_info*[cache_size];
	free_list = new int(cache_size);
	for(int i=0; i<cache_size; i++){
		cache[i] = new Cache_info();
		cache[i]->dirty = cache[i]->valid = cache[i]->age = 0;
		cache[i]->lbn = -1;
		free_list[free_count++] = i;
	}

	valid_set.clear();
	valid_map.clear();
//	while(!LRU_queue.empty()) LRU_queue.pop();
}

int Cache_dev::is_incache(int page){
	map<int,int>::iterator itr;
	if((itr = valid_map.find(page)) != valid_map.end()){
		return itr->second;
	}
	return -1;
}

void Cache_dev::push_cache(int page, int index){
	assert(free_count >= 1);
//	int index = free_list[free_count];
	cache[index]->valid = 1;
	cache[index]->dirty = 0;
	cache[index]->lbn = page;
	cache[index]->age = 0;
	free_count--;

	valid_map[page] = index;
}

//void Cache_dev::put_cache(int index, int page){
//	//int index = find_empty(page);
//	assert(index >= 0);
//	cache[index]->valid = TRUE;
//	cache[index]->lbn = page;
//	valid_set.insert(page);
//	curr_size++;
//}

int Cache_dev::find_lru_victim(){
	int max_access = 999999, index = -1;
	for(int i=0;i < cache_size;i++){
		if(cache[i]->age < max_access){
			max_access = cache[i]->age;
			index = i;
		}
	}
	return index;
}

void Cache_dev::make_cache_dirty(int page, int index){
	assert(index >= 0 && index < cache_size);
	cache[index]->dirty = TRUE;
//	map<int,int>::iterator itr;
//	if((itr = valid_map.find(page)) != valid_map.end()){
//		cache[itr->second]->age++;
//	}else{
//		cout << "making dirty page error" << endl;
//	}
}

bool Cache_dev::is_cache_full(){
	if(free_count == 0)
		return true;
	return false;
}

void Cache_dev::replace_victim_cache(int index){
	int page = cache[index]->lbn;

	assert(index >= 0);

	cache[index]->valid = FALSE;
	free_list[free_count++] = index;
	valid_map.erase(page);

	if(cache[index]->dirty == FALSE)
		return;

	ioreq_event * event = new ioreq_event();
	event->time = current_time;
	event->type = IO_SEVERCE_REQUEST;
	event->blkno = 	cache[index]->lbn;
	event->bcount = 4;
	event->flag = VICTIM_IO_REQUEST;
	event->operation = WRITE;

	write_out_trace(event);
	//insert_io_event(event);

	curr_size--;
}

void Cache_dev::write_out_trace(ioreq_event* q){
	if(q)
		fprintf(outfile, "%lf %d %d %d %d\n", q->time, q->devno, q->blkno, q->bcount, q->operation);
	else
		printf("Error: writing to out trace file");
	fflush(outfile);
}
int Cache_dev::get_empty_slot(){
	assert(free_count >= 1);
	return free_list[free_count-1];
}
void Cache_dev::send_cache_request(ioreq_event * curr){
	int start_page, end_page, page, index;
	int empty_index;

	current_time = curr->time;
	//change the count as 4KB page from 512B sector

	end_page = (curr->blkno + curr->bcount - 1) / 4;
//	end_page *= 4;
	start_page = curr->blkno / 4;
//	start_page *= 4;

	for(page = start_page; page <= end_page; page++){
		if((index = is_incache(page)) >= 0){
			cache_stat->cache_hit++;
			cache[index]->age++;
		}else{
			cache_stat->cache_miss++;
			if(is_cache_full()){
				empty_index = find_lru_victim();
				replace_victim_cache(empty_index);
			}else {
				empty_index = get_empty_slot();
			}

			push_cache(page, empty_index);

			if(curr->operation == WRITE)
				make_cache_dirty(page, empty_index);
		}
	}
}

